use dotenvy::dotenv;
use lazy_static::lazy_static;
use serde::Deserialize;
use std::env;
use std::fmt;

#[derive(Debug, Default, Deserialize)]
pub struct AppConfig {
    pub server_addr: String,
    pub port: u16,
    pub auth: Option<Auth>,
    pub api_mapping: Option<Vec<ApiMapping>>,
    pub static_mapping: Option<Vec<UrlMapping>>,
}

fn build_app_config() -> Option<AppConfig> {
    AppConfig::from_env()
}

impl AppConfig {
    pub fn from_env() -> Option<Self> {
        dotenv().ok(); // 加载.env文件

        Some(AppConfig {
            server_addr: dotenvy::var("SERVER_ADDR").ok()?,
            port: dotenvy::var("PORT").ok()?.parse::<u16>().ok()?,
            auth: load_auth(),
            api_mapping: load_api_prefixes().map(|prefixes| {
                prefixes
                    .into_iter()
                    .filter_map(|prefix| load_api_mapping(&prefix))
                    .collect::<Vec<_>>()
            }),
            static_mapping: load_static_prefixes().map(|prefixes| {
                prefixes
                    .into_iter()
                    .filter_map(|prefix| load_static_mapping(&prefix))
                    .collect::<Vec<_>>()
            }),
        })
    }
}

pub fn load_api_prefixes() -> Option<Vec<String>> {
    load_prefixes("api_prefixes")
}

pub fn load_static_prefixes() -> Option<Vec<String>> {
    load_prefixes("static_prefixes")
}

fn load_prefixes(prefix: &str) -> Option<Vec<String>> {
    dotenvy::var(prefix.to_uppercase()).ok().map(|result| {
        result
            .split(',')
            .map(|e| e.trim().to_string().to_uppercase())
            .collect()
    })
}

fn to_bool(val: Option<&str>) -> bool {
    match val {
        Some(val_1) => val_1.parse::<bool>().unwrap_or(false),
        _ => false,
    }
}

fn load_env_var(key: &str, var_name: &str) -> Option<String> {
    dotenvy::var(format!(
        "{}_{}",
        key.to_uppercase(),
        var_name.to_uppercase()
    ))
    .ok()
}
pub fn load_api_mapping(prefix: &str) -> Option<ApiMapping> {
    let prefix1 = load_env_var(prefix, "prefix")?;
    let target = load_env_var(prefix, "target")?;
    let replace = to_bool(load_env_var(prefix, "replace").as_deref());
    let need_check_auth = to_bool(load_env_var(prefix, "need_check_auth").as_deref());

    Some(ApiMapping {
        prefix: prefix1,
        target,
        replace,
        need_check_auth,
    })
}

pub fn load_static_mapping(prefix: &str) -> Option<UrlMapping> {
    let prefix1 = load_env_var(prefix, "prefix")?;
    let target = load_env_var(prefix, "target")?;
    let replace = to_bool(load_env_var(prefix, "replace").as_deref());

    Some(UrlMapping {
        prefix: prefix1,
        target,
        replace,
    })
}

pub fn load_auth() -> Option<Auth> {
    let redis_addr = dotenvy::var("AUTH_REDIS_ADDR").ok()?;
    let redis_port_str = dotenvy::var("AUTH_REDIS_PORT").ok()?;
    let auth_url = dotenvy::var("AUTH_URL").ok()?;

    let redis_port = redis_port_str.parse::<u16>().ok()?;

    Some(Auth {
        redis_addr: redis_addr,
        redis_port: redis_port,
        auth_url: auth_url,
    })
}

/*
fn load_api_mappings() -> Result<Option<Vec<ApiMapping>>, std::env::VarError> {
    match env::var("API_MAPPINGS") {
        Ok(mappings_json) => {
            let mappings: Vec<ApiMapping> = serde_json::from_str(&mappings_json)?;
            Ok(Some(mappings))
        }
        Err(_) => Ok(None),
    }
}

fn load_static_mappings() -> Result<Option<Vec<UrlMapping>>, std::env::VarError> {
    // 假设UrlMapping结构体已经定义好了
    #[derive(Debug, Deserialize)]
    struct UrlMapping {
        from: String,
        to: String,
    }

    match env::var("STATIC_MAPPINGS") {
        Ok(mappings_json) => {
            let mappings: Vec<UrlMapping> = serde_json::from_str(&mappings_json)?;
            Ok(Some(mappings))
        }
        Err(_) => Ok(None),
    }
}
*/

impl fmt::Display for AppConfig {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "Server Addr: {}", self.server_addr)?;
        writeln!(f, "Port: {}", self.port)?;

        if let Some(ref auth) = self.auth {
            writeln!(f, "Auth: {:?}", auth)?; // 假设Auth没有实现Display，这里使用Debug
        } else {
            writeln!(f, "Auth: None")?;
        }

        if let Some(ref api_mappings) = self.api_mapping {
            writeln!(f, "API Mappings:")?;
            for mapping in api_mappings {
                writeln!(f, "- {}", mapping)?;
            }
        } else {
            writeln!(f, "API Mappings: None")?;
        }

        if let Some(ref static_mappings) = self.static_mapping {
            writeln!(f, "Static Mappings:")?;
            for mapping in static_mappings {
                writeln!(f, "- {}", mapping)?;
            }
        } else {
            writeln!(f, "API Mappings: None")?;
        }
        Ok(())
    }
}
lazy_static! {
    static ref APP_CONFIG: AppConfig = build_app_config().expect("AppConfig加载失败");
}
pub fn get_app_config() -> &'static AppConfig {
    &APP_CONFIG
}

#[derive(Deserialize, Debug, Clone)]
pub struct ApiMapping {
    pub prefix: String,
    pub target: String,
    pub replace: bool,
    pub need_check_auth: bool,
}

#[derive(Deserialize, Debug, Clone)]
pub struct ApiMappingEnv {
    pub prefix: String,
    pub target: String,
    pub replace: bool,
    pub need_check_auth: bool,
}

impl From<ApiMapping> for ApiMappingEnv {
    fn from(val: ApiMapping) -> Self {
        let env_target = dotenvy::var(val.prefix.clone().replace('/', "_")).ok();

        Self {
            prefix: val.prefix,
            target: env_target.unwrap_or(val.target),
            replace: val.replace,
            need_check_auth: val.need_check_auth,
        }
    }
}

impl fmt::Display for ApiMapping {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "ApiMapping {{ prefix: {}, target: {}, replace: {}, need_check_auth: {} }}",
            self.prefix, self.target, self.replace, self.need_check_auth
        )
    }
}
pub trait MappingItem {
    fn prefix(&self) -> String;
    fn target(&self) -> String;
    fn replace(&self) -> bool;
}

impl MappingItem for ApiMappingEnv {
    fn prefix(&self) -> String {
        self.prefix.clone()
    }
    fn target(&self) -> String {
        self.target.clone()
    }
    fn replace(&self) -> bool {
        self.replace
    }
}

pub fn get_target<'a, T>(items: &'a [T], url: &str) -> Option<&'a T>
where
    T: MappingItem,
{
    items
        .iter()
        .find(|&item| url.starts_with(&format!("{}/", item.prefix())))
}
#[derive(Deserialize, Debug, Clone)]
pub struct UrlMapping {
    pub prefix: String,
    pub target: String,
    pub replace: bool,
}
impl fmt::Display for UrlMapping {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "UrlMapping {{ prefix: {}, target: {}, replace: {} }}",
            self.prefix, self.target, self.replace
        )
    }
}

#[derive(Deserialize, Debug, Clone)]
pub struct UrlMappingEnv {
    pub prefix: String,
    pub target: String,
    pub replace: bool,
}

impl From<UrlMapping> for UrlMappingEnv {
    fn from(val: UrlMapping) -> Self {
        let env_target = dotenvy::var(val.prefix.clone().replace('/', "_")).ok();

        Self {
            prefix: val.prefix,
            target: env_target.unwrap_or(val.target),
            replace: val.replace,
        }
    }
}
impl MappingItem for UrlMappingEnv {
    fn prefix(&self) -> String {
        self.prefix.clone()
    }
    fn target(&self) -> String {
        self.target.clone()
    }
    fn replace(&self) -> bool {
        self.replace
    }
}

impl From<ApiMapping> for UrlMapping {
    fn from(val: ApiMapping) -> Self {
        Self {
            prefix: val.prefix,
            target: val.target,
            replace: val.replace,
        }
    }
}

pub struct ApiMappingList(pub Vec<ApiMappingEnv>);
pub struct StaticMappingList(pub Vec<UrlMappingEnv>);

#[derive(Debug, Default, Deserialize)]
pub struct Auth {
    pub redis_addr: String,
    pub redis_port: u16,
    pub auth_url: String,
}
